/* ****************************************************************************
 *
 * Copyright (c) Microsoft Corporation. 
 *
 * This source code is subject to terms and conditions of the Microsoft Public License. A 
 * copy of the license can be found in the License.html file at the root of this distribution. If 
 * you cannot locate the  Microsoft Public License, please send an email to 
 * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound 
 * by the terms of the Microsoft Public License.
 *
 * You must not remove this notice, or any other, from this software.
 *
 * THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
 * KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
 * PARTICULAR PURPOSE.
 * ***************************************************************************/


#include "stdafx.h"
#include <pgl/core/PGLLine2DLOD.h>
#include <pgl/core/PGLLine2DLODPropPage.h>
#include <pgl/core/PGLRegion.h>

PGL_IMPLEMENT_DYNCREATE(CPGLLine2DLOD, CPGLLine2D);
PGL_IMPLEMENT_SCRIPTING(Line2DLOD);

//! Save settings
void CPGLLine2DLOD::SerializeXML(CPGLArchive& ar)
{
	static const TCHAR szTag[] = _T("CPGLLine2DLOD");

	markup::CMarkupArchive& xml = *((markup::CMarkupArchive*)ar.GetMarkup());
	if(ar.IsStoring())
	{
		xml.AddChildElem(szTag);
		xml.AddChildAttrib(_T("Name"),GetName());
		xml.IntoElem();
		
			// base class
			CPGLLine2D::SerializeXML(ar);

			xml.AddChildElemEx(_T("Tol"),m_hull.GetTol());
		xml.OutOfElem();
	}
	else
	{
		if  (!xml.FindChildElem(szTag))
				return;

		xml.IntoElem();

			/////////////////////////////////////////////////////////
			// loading base class
			CPGLLine2D::SerializeXML(ar);

			if (xml.FindChildElem(_T("Tol")))
				m_hull.SetTol( __max(0,xml.GetChildDataFloat()) );

		xml.OutOfElem();

		ResetHull();
		NeedUpdateExtent();
	}

}

#ifdef _DEBUG
void CPGLLine2DLOD::Dump( CDumpContext& dc ) const
{
    // call base class function first
    CPGLLine2D::Dump( dc );

    // now do the stuff for our specific class
	dc << _T("CPGLLine2DLOD ID ") << GetID() << "\n";
	dc <<"Tol : "<<m_hull.GetTol()<<"\n";
}
void CPGLLine2DLOD::AssertValid() const
{
    // call inherited AssertValid first
    CPGLLine2D::AssertValid();

} 
#endif


CPGLLine2DLOD::CPGLLine2DLOD()
: CPGLLine2D()
{
	LoadBitmap(IDB_PGL_LINE2DLOD_BITMAP);

	m_pDPX = CPGLVectorFPtr(new CPGLVectorF());
	m_pDPY = CPGLVectorFPtr(new CPGLVectorF());
	m_hull.GetKeys(m_pDPX->GetV(),m_pDPY->GetV());

	PreparePropertyMap();
}

CPGLLine2DLOD& CPGLLine2DLOD::operator = (const CPGLLine2DLOD &l)
{
	if (this!=&l)
	{
		this->CPGLLine2D::operator=(l);

		// Copying tol...
		m_hull.SetTol(l.m_hull.GetTol());
		
		ResetHull();

		LoadBitmap(IDB_PGL_LINE2DLOD_BITMAP);
	}
	return *this;
}

void CPGLLine2DLOD::AddContextMenuItems(CMenu* pMenu)
{
	ASSERT_VALID(pMenu);
	// first call base class function
	CPGLLine2D::AddContextMenuItems(pMenu);
	
}

void CPGLLine2DLOD::AddPropertyPage(CPropertySheet* pPropSheet)
{
	ASSERT_VALID(pPropSheet);
	// call own functions
	CPGLLine2DLODPropPage* propPage=new CPGLLine2DLODPropPage(this);
	pPropSheet->AddPage(propPage);

	// first call base class function
	CPGLLine2D::AddPropertyPage(pPropSheet);
}

CPGLLine2DLOD::~CPGLLine2DLOD()
{
	m_pDPX.reset();
	m_pDPY.reset();
}

/// plotting to EPS file
void CPGLLine2DLOD::PlotGfx(gfxinterface::CGfxInterface& gfx)
{
	// first if it is visible
	if (!IsVisible())
		return;

	// calling base class plot
	CPGLLine2D::PlotGfx(gfx);
}


void CPGLLine2DLOD::SetDatas(const std::vector<double>& vx, const std::vector<double>& vy, bool bMakeCopy)
{
	CPGLLine2D::SetDatas(vx, vy, bMakeCopy);
	ResetHull();
}
void CPGLLine2DLOD::SetDatas(const std::vector<float>& vx, const std::vector<float>& vy, bool bMakeCopy)
{
	CPGLLine2D::SetDatas(vx, vy, bMakeCopy);
	ResetHull();
}
void CPGLLine2DLOD::SetDatas(const std::vector<int>& vx, const std::vector<int>& vy, bool bMakeCopy)
{
	CPGLLine2D::SetDatas(vx, vy, bMakeCopy);
	ResetHull();
}
void CPGLLine2DLOD::SetDatas(const std::vector<BYTE>& vx, const std::vector<BYTE>& vy, bool bMakeCopy)
{
	CPGLLine2D::SetDatas(vx, vy, bMakeCopy);
	ResetHull();
}

void CPGLLine2DLOD::SetDatas(CPGLDataPtr pX, CPGLDataPtr pY)
{
	CPGLLine2D::SetDatas(pX,pY);
	ResetHull();
}

size_t CPGLLine2DLOD::ShrinkNorm( float fScale, float fScaleTol, size_t nMaxIter)	
{	
	m_hull.ShrinkNorm(fScale, fScaleTol, -1, nMaxIter);
	m_hull.GetKeys(m_pDPX->GetV(),m_pDPY->GetV());
	return m_hull.GetKeys().size();	
};

size_t CPGLLine2DLOD::Shrink( size_t nDesiredPoints, size_t nTol, size_t nMaxIter)
{	
	m_hull.Shrink(nDesiredPoints, nTol, -1, nMaxIter);
	m_hull.GetKeys(m_pDPX->GetV(),m_pDPY->GetV());
	return m_hull.GetKeys().size();	
};

void CPGLLine2DLOD::UpdateExtent(gfxinterface::CGfxInterface& gfx)
{
	// Calling base class function
	CPGLLine2D::UpdateExtent(gfx);

	// Approximating
	m_hull.Simplify();
	m_hull.GetKeys(m_pDPX->GetV(),m_pDPY->GetV());
}

void CPGLLine2DLOD::SetTol(float tol)
{
	m_hull.SetTol(tol);

	// Approximating
	m_hull.Simplify();
	m_hull.GetKeys(m_pDPX->GetV(),m_pDPY->GetV());
}

void CPGLLine2DLOD::PlotLineStripGfx(gfxinterface::CGfxInterface& gfx)
{
	if (!IsContoured())
		return;

	DWORD lineType = GetELineType();

	ASSERT(m_pDPX);
	ASSERT(m_pDPY);

	switch (GetInterpolationType())
	{
	case InterpolationTypeStep:
		gfx.DrawStepStrip(*m_pDPX, *m_pDPY, lineType );
		break;
	case InterpolationTypeLinear:
		gfx.DrawLineStrip(*m_pDPX, *m_pDPY, lineType );
		break;
	case InterpolationTypeSpline:
		gfx.DrawLineStrip(*m_pDPX, *m_pDPY, lineType , m_dTension);
		break;
	case InterpolationTypeSegment:
		gfx.DrawMultipleLineStrip(m_iStripSize,*m_pDPX, *m_pDPY, lineType );
		break;
	}
}

void CPGLLine2DLOD::PlotPointStripGfx(gfxinterface::CGfxInterface& gfx)
{
	ASSERT(m_pDPX);
	ASSERT(m_pDPY);
	m_pMarker->PlotGfx( gfx, m_pDPX, m_pDPY);
}

void CPGLLine2DLOD::ResetHull()
{
	// setting curve
	LineApprox::PointContainer& pc=m_hull.GetPoints();
	pc.resize(GetNPoints());

	// filling structure
	for (size_t i=0;i<GetNPoints();i++)
	{
		pc[i].x=(*m_pX)[i];
		pc[i].y=(*m_pY)[i];
	}
	m_hull.ComputeBoundingBox();

	m_hull.Simplify();
	m_hull.GetKeys(m_pDPX->GetV(),m_pDPY->GetV());
}


void CPGLLine2DLOD::PreparePropertyMap()
{
	using namespace std;
	static bool bLoaded(false);
	
	if (bLoaded)
		return;
	bLoaded=true;

	CPGLLine2D::PreparePropertyMap();

	m_mProps[_T("Line2DLOD.shrinktol")]=PropertyPair(Line2DLODPropShrinkTol, _T("tolerance, positive double"));
	m_mProps[_T("Line2DLOD.shrinknorm")]=PropertyPair(Line2DLODPropShrinkNorm, _T("scale = [0,1], scaletol = [0,1], maxiter = maximum number of iterations"));
	m_mProps[_T("Line2DLOD.shrinkpoint")]=PropertyPair(Line2DLODPropShrinkPoint, _T("scale = [0, number of points], scaletol = npoints, maxiter = maximum number of iterations"));
}

bool CPGLLine2DLOD::SetProperty( LPCTSTR szProp, LPCTSTR szValue)
{
	if (CPGLLine2D::SetProperty(szProp, szValue))
		return true;

	parsers::CCmdParser parser;
	CString sv(szValue);
	sv.MakeLower();

	CString val;
	UINT u,maxIter=100;
	float f=0;
	float fScale=0.1f;
	float fScaleTol=0.05f; 
	UINT uScale = GetNPoints()/10, uScaleTol = uScale/20; 
	switch( StringToLine2DLODProp( szProp ))
	{
	case Line2DLODPropShrinkTol:
		if (_stscanf(sv, _T("%f"), &f) == 1)
			SetTol( f );
		return true;
	case Line2DLODPropShrinkNorm:
		if(!parser.Parse(szValue))
			return true;

		if (parser.HasKey(_T("scale"), val) && _stscanf( val, _T("%f"), &f) == 1)
			fScale = f;
		if (parser.HasKey(_T("scaletol"), val) && _stscanf( val, _T("%f"), &f) == 1)
			fScaleTol = f;
		if (parser.HasKey(_T("maxiter"), val) && _stscanf( val, _T("%d"), &u) == 1)
			maxIter = u;

		ShrinkNorm( fScale, fScaleTol, maxIter );
		return true;
	case Line2DLODPropShrinkPoint:
		if(!parser.Parse(szValue))
			return true;

		if (parser.HasKey(_T("scale"), val) && _stscanf( val, _T("%d"), &u) == 1)
			uScale = u;
		if (parser.HasKey(_T("scaletol"), val) && _stscanf( val, _T("%d"), &u) == 1)
			uScaleTol = u;
		if (parser.HasKey(_T("maxiter"), val) && _stscanf( val, _T("%d"), &u) == 1)
			maxIter = u;

		Shrink( uScale, uScaleTol, maxIter );
		return true;
	default:
		return false;
	};
}

bool CPGLLine2DLOD::Get( LPCTSTR szProp, CString& str)
{
	if(CPGLLine2D::Get(szProp, str))
		return true;

	switch( StringToLine2DLODProp( szProp ))
	{
	case Line2DLODPropShrinkTol:
		str.Format(_T("%g"), GetTol() );
		return true;
	case Line2DLODPropShrinkNorm:
		str.Format(_T("%g"), m_hull.GetKeySize()/(double)m_hull.GetPointSize() );
		return true;
	case Line2DLODPropShrinkPoint:
		str.Format(_T("%d"), m_hull.GetKeySize());
		return true;
	default:
		return false;
	};
}
